﻿using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using DotNetOpenAuth.OpenId.Provider;
using DotNetOpenAuth.OpenId.Provider.Behaviors;
using SumPower.MailAccount.Web.Models;
using SumPower.MailAccount.Web.XMail263Service;
using System;
using System.Configuration;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using System.Xml;

namespace SumPower.MailAccount.Web.Controllers
{
    [Authorize]
    public class OpenIdController : Controller
    {
        private static OpenIdProvider openIdProvider = new OpenIdProvider();

        private readonly string key;
        private readonly string domain;
        private readonly string account;
        public OpenIdController()
        {
            //this.authenticateService = authenticateService;
            key = ConfigurationManager.AppSettings["key"];
            domain = ConfigurationManager.AppSettings["domain"];
            account = ConfigurationManager.AppSettings["account"];
        }
        /// <summary>
        /// 提供OP标识的XRDS信息
        /// </summary>
        /// <returns></returns>
        [AllowAnonymous]
        public ActionResult Identifier(bool? force_login)
        {
            if (User.Identity.IsAuthenticated && ProviderEndpoint.PendingAuthenticationRequest != null)
            {
                return AskUser();
            }

            if (Request.AcceptTypes != null && Request.AcceptTypes.Contains("application/xrds+xml"))
            {
                //return Xrds(force_login);
                return Xrds();
            }
            return View();
        }

        public ActionResult Xrds()
        {
            Response.ContentType = "application/xrds+xml";
            ViewBag.ServiceUri = new Uri(Request.Url, Url.Action("Provider"));

            return View("Xrds");
        }


        /// <summary>
        /// 因为RP提交的数据中会包含一些被.NET认为危险的代码，故需禁用验证。
        /// </summary>
        /// <returns></returns>
        [ValidateInput(false)]
        [AllowAnonymous]
        public ActionResult Provider()
        {
            if (User.Identity.IsAuthenticated && this.Request.HttpMethod.ToLower().Equals("get"))
            {
                FormsAuthentication.SignOut();
                Thread.CurrentPrincipal = HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
            }

            IRequest request = openIdProvider.GetRequest();
            if (request == null)
            {
                //不是OP发过来的请求。
                return RedirectToAction("Index", "Home");
            }

            if (request.IsResponseReady)
            {
                //如果请求的响应已经处理完成，立即返回。
                return openIdProvider.PrepareResponse(request).AsActionResultMvc5();
            }

            ProviderEndpoint.PendingRequest = (IHostProcessedRequest)request;

            var policyRequest = ProviderEndpoint.PendingRequest.GetExtension<PolicyRequest>();
            if (policyRequest != null && policyRequest.MaximumAuthenticationAge.HasValue)
            {
                //验证最近已登录用户的信息，如：过期策略
                TimeSpan timeSinceLogin = DateTime.Now - DateTime.Now;// -authenticateService.GetAuthenticatedUtcTime().Value;
                if (timeSinceLogin > policyRequest.MaximumAuthenticationAge.Value)
                {
                    //通过重新登录获取更新的用户信息。
                    return this.RedirectToAction("SignIn", "Account", new { returnUrl = this.Url.Action("ProcessAuthenticatedRequest") });
                }
            }

            return this.ProcessAuthenticatedRequest();

        }

        public ActionResult ProcessAuthenticatedRequest()
        {
            if (ProviderEndpoint.PendingRequest == null)
            {
                throw new NotSupportedException("PendingRequest");
                //return this.RedirectToAction("Index", "Home");
            }

            // Try responding immediately if possible.
            ActionResult response;
            if (this.AutoResponse(out response))
            {
                return response;
            }

            // We can't respond immediately with a positive result.  But if we still have to respond immediately...
            if (ProviderEndpoint.PendingRequest.Immediate)
            {
                // We can't stop to prompt the user -- we must just return a negative response.
                return this.SendAssertion();
            }

            return this.RedirectToAction("AskUser");
        }

        /// <summary>
        /// 显示用户确认RP信息页
        /// </summary>
        /// <returns></returns>
        public ActionResult AskUser()
        {
            if (ProviderEndpoint.PendingRequest == null)
            {
                //不是OP请求
                return this.RedirectToAction("Index", "Home");
            }

            //立即自动响应请求，而无需用户确认。
            ActionResult response;
            if (this.AutoResponse(out response))
            {
                return response;
            }
            //需提供友好信息，而不是简单的URL
            this.ViewData["Realm"] = ProviderEndpoint.PendingRequest.Realm;

            return this.View();
        }

        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult AskUserResponse(string action)
        {
            bool confirmed = action.Equals("confirm");
            if (ProviderEndpoint.PendingAnonymousRequest != null)
            {
                ProviderEndpoint.PendingAnonymousRequest.IsApproved = confirmed;
            }
            else if (ProviderEndpoint.PendingAuthenticationRequest != null)
            {
                ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = confirmed;
            }
            else
            {
                throw new InvalidOperationException("There's no pending authentication request!");
            }

            return this.SendAssertion();
        }

        private string Md5Sign(string value)
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] buffer = Encoding.Default.GetBytes(value);
            string result = BitConverter.ToString(md5.ComputeHash(buffer));
            return result.Replace("-", "").ToLower();

        }

        private UserInfo GetUserInfo(string userName)
        {
            var userNameP = userName.Split('@');
            if (userNameP[1].Equals("samples.cn"))
            {
                XmapiImplService xmapiImplService = new XmapiImplService();
                string sign = Md5Sign(userNameP[0].ToString() + domain + account + key);
                string userXml = xmapiImplService.getUserInfo_New(userNameP[0].ToString(), domain, account, sign);
                if (!string.IsNullOrEmpty(userXml))
                {
                    UserInfo user = new UserInfo();
                    XmlDocument xmlDocument = new XmlDocument();
                    xmlDocument.LoadXml(userXml);

                    XmlNode userNode = xmlDocument.SelectSingleNode("user");
                    user.Userid = userNode["userid"].InnerText;
                    user.Name = userNode["name"].InnerText;
                    user.Departmentid = userNode["departmentid"].InnerText;
                    user.Office = userNode["office"].InnerText;
                    user.Mobile = userNode["mobile"].InnerText;
                    user.Phone = userNode["phone"].InnerText;
                    user.Fax = userNode["fax"].InnerText;
                    return user;
                }
            }
            else
            {
                UserInfo user = new UserInfo();
                user.Userid = userName;
                user.Name = userName;
                return user;
            }
            return null;
        }

        public ActionResult SendAssertion()
        {
            var pendingRequest = ProviderEndpoint.PendingRequest;
            ProviderEndpoint.PendingRequest = null; // clear session static so we don't do this again
            if (pendingRequest == null)
            {
                throw new InvalidOperationException("There's no pending authentication request!");
            }
            UserInfo user = (Session["User"] as UserInfo) ?? GetUserInfo(User.Identity.Name);

            var anonymousRequest = pendingRequest as IAnonymousRequest;
            if (anonymousRequest != null && !anonymousRequest.IsApproved.HasValue)
            {
                anonymousRequest.IsApproved = false;
            }

            var authenticationRequest = pendingRequest as IAuthenticationRequest;
            if (authenticationRequest != null && !authenticationRequest.IsAuthenticated.HasValue)
            {
                authenticationRequest.IsAuthenticated = false;
            }
            if (authenticationRequest != null && authenticationRequest.IsAuthenticated.Value)
            {
                if (authenticationRequest.IsDirectedIdentity)
                {
                    //authenticationRequest.LocalIdentifier = new Uri(Request.Url, Url.Content("~/user/" + User.Identity.Name));
                    Uri identifier = null;
                    if (!Uri.TryCreate(User.Identity.Name, UriKind.Absolute, out identifier))
                    {
                        identifier = new Uri(Request.Url, Url.Content("~/user/" + User.Identity.Name)); ;
                    }
                    authenticationRequest.LocalIdentifier = identifier;
                }

                if (!authenticationRequest.IsDelegatedIdentifier)
                {
                    authenticationRequest.ClaimedIdentifier = authenticationRequest.LocalIdentifier;
                }
                //var fetchRequest = authenticationRequest.GetExtension<FetchRequest>();
                //if (fetchRequest != null)
                //{
                //    var fetchResponse = new FetchResponse();
                //    if (fetchRequest.Attributes.Contains(WellKnownAttributes.Name.FullName))
                //    {
                //        fetchResponse.Attributes.Add(WellKnownAttributes.Name.FullName, User.Identity.Name);
                //    }
                //    authenticationRequest.AddResponseExtension(fetchResponse);
                //}
            }

            // Respond to AX/sreg extension requests only on a positive result.
            if ((authenticationRequest != null && authenticationRequest.IsAuthenticated.Value) ||
                (anonymousRequest != null && anonymousRequest.IsApproved.Value))
            {
                #region ClaimsRequest
                // Look for a Simple Registration request.  When the AXFetchAsSregTransform behavior is turned on
                // in the web.config file as it is in this sample, AX requests will come in as SReg requests.
                var claimsRequest = pendingRequest.GetExtension<ClaimsRequest>();
                if (claimsRequest != null)
                {
                    var claimsResponse = claimsRequest.CreateResponse();

                    // This simple respond to a request check may be enhanced to only respond to an individual attribute
                    // request if the user consents to it explicitly, in which case this response extension creation can take
                    // place in the confirmation page action rather than here.
                    if (claimsRequest.Email != DemandLevel.NoRequest)
                    {
                        // claimsResponse.Email = User.Identity.Name + "@samples.cn";
                        claimsResponse.Email = User.Identity.Name;
                    }
                    if (claimsRequest.FullName != DemandLevel.NoRequest)
                    {
                        claimsResponse.FullName = user.Name;
                    }
                    pendingRequest.AddResponseExtension(claimsResponse);
                }
                #endregion
                #region FetchRequest
                var fetchRequest = pendingRequest.GetExtension<FetchRequest>();
                if (fetchRequest != null)
                {
                    var fetchResponse = new FetchResponse();

                    if (fetchRequest.Attributes.Contains(WellKnownAttributes.Contact.Email))
                    {
                        fetchResponse.Attributes.Add(WellKnownAttributes.Name.FullName
                            , User.Identity.Name);
                    }
                    pendingRequest.AddResponseExtension(fetchRequest);
                }
                #endregion

                // Look for PAPE requests.
                var policyRequest = pendingRequest.GetExtension<PolicyRequest>();
                if (policyRequest != null)
                {
                    var policyResponse = new PolicyResponse();
                    if (policyRequest.MaximumAuthenticationAge.HasValue)
                    {
                        policyResponse.AuthenticationTimeUtc = DateTime.UtcNow;// authenticateService.GetAuthenticatedUtcTime();//? this.FormsAuth.SignedInTimestampUtc;
                    }

                    pendingRequest.AddResponseExtension(policyResponse);
                }
            }

            var response = openIdProvider.PrepareResponse(pendingRequest);

            return response.AsActionResultMvc5();
        }

        //private bool IsRegisterHosts(IHostProcessedRequest request)
        //{
        //    return request.Realm.Host.ToLower().Equals(this.Request.Url.Host.ToLower());
        //}

        private bool IsAllowedHosts(IHostProcessedRequest request)
        {
            bool result = request.Realm.Host.ToLower().Equals(this.Request.Url.Host.ToLower());

            if (ConfigurationManager.AppSettings.AllKeys.Contains("whitelistHosts"))
            {
                var whitelistHosts = ConfigurationManager.AppSettings["whitelistHosts"].Split(';');
                foreach (var host in whitelistHosts)
                {
                    Regex regex = new Regex(string.Format("({0})$", host.Trim()), RegexOptions.IgnoreCase);
                    if (regex.IsMatch(request.Realm.Host))
                    {
                        return true;
                    }
                }
            }
            return result;
        }
        private bool AutoResponse(out ActionResult responseActionResult)
        {
            if (
                (IsAllowedHosts(ProviderEndpoint.PendingRequest) ||
                ProviderEndpoint.PendingRequest.IsReturnUrlDiscoverable(openIdProvider.Channel.WebRequestHandler) == RelyingPartyDiscoveryResult.Success)
                && User.Identity.IsAuthenticated
                && this.HasUserAuthorizedAutoLogin(ProviderEndpoint.PendingRequest))
            {
                if (ProviderEndpoint.PendingAuthenticationRequest != null)
                {
                    if (ProviderEndpoint.PendingAuthenticationRequest.IsDirectedIdentity
                        || this.UserControlsIdentifier(ProviderEndpoint.PendingAuthenticationRequest))
                    {
                        ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = true;
                        responseActionResult = this.SendAssertion();
                        return true;
                    }
                }

                if (ProviderEndpoint.PendingAnonymousRequest != null)
                {
                    ProviderEndpoint.PendingAnonymousRequest.IsApproved = true;
                    responseActionResult = this.SendAssertion();
                    return true;
                }
            }

            responseActionResult = null;
            return false;
        }

        /// <summary>
        /// 确认用户是否授权允许自动跳转回RP，即提供他的身份信息。
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        private bool HasUserAuthorizedAutoLogin(IHostProcessedRequest request)
        {
            if (true/*User.UserLikesRP(request.Realm))*/)
            {
                //通过用户曾经确认的记录来判断用户是否允许自动响应。
                if (true/*User.HasGrantedExtensions(request)*/)
                {
                    if (request.GetExtension<ClaimsRequest>() != null)
                    {
                        //return false;
                    }

                    return true;
                }
            }
        }

        /// <summary>
        /// Checks whether the logged in user controls the OP local identifier in the given authentication request.
        /// </summary>
        /// <param name="authenticationRequest">The authentication request.</param>
        /// <returns><c>true</c> if the user controls the identifier; <c>false</c> otherwise.</returns>
        private bool UserControlsIdentifier(IAuthenticationRequest authenticationRequest)
        {
            if (User == null || User.Identity == null)
            {
                return false;
            }
            Uri userLocalIdentifier = new Uri(Url.RouteUrl(MvcApplication.UserIdentifierRouteName, User.Identity.Name));
            return authenticationRequest.LocalIdentifier == userLocalIdentifier ||
                authenticationRequest.LocalIdentifier == PpidGeneration.PpidIdentifierProvider.GetIdentifier(userLocalIdentifier, authenticationRequest.Realm);
        }

    }
}